/*
 * Code by J. Philip, license MIT
 * Partial class to make a model sluggable
 */

using System;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Collections.Generic;
using NHibernate.Expression;
using Castle.ActiveRecord;

namespace Wks.Models
{
    /// <summary>
    /// Sets a unique identifier based on field and a separator
    /// </summary>
    public partial class MyActiveRecord<T> : ActiveRecordValidationBase<T> where T: class
    {
        static Dictionary<char, string> charMap = new Dictionary<char, string>();

        protected override void OnSave()
        {
            SetSlug();
            base.OnSave();
        }
                
        public virtual void SetSlug()
        {   
            Type t = this.GetType();
            SluggableAttribute attr = (SluggableAttribute)Attribute.GetCustomAttribute(t, typeof(SluggableAttribute));
            if (attr == null)
                throw new Exception("Sluggable Attribute not found");
            PropertyInfo proposalInfo = t.GetProperty(attr.ProposalProperty);
            if (proposalInfo == null)
                throw new Exception("Invalid ProposalProperty");
            string proposal = proposalInfo.GetValue(this, null).ToString();
            if (string.IsNullOrEmpty(proposal))
                throw new Exception("Invalid Proposal Value");
            PropertyInfo slugInfo = t.GetProperty(attr.SlugProperty);
            if (slugInfo == null)
                throw new Exception("Invalid slugProperty");
            proposal = Slugify(proposal, attr.Separator);
            using(new SessionScope())
            {
                ICriterion criterion = Expression.Like(attr.SlugProperty, proposal, MatchMode.Start);
                ICriterion[] criterions;
                if (string.IsNullOrEmpty(attr.ScopeProperty))
                    criterions = new ICriterion[] {criterion};
                else
                {
                    PropertyInfo scopeInfo = t.GetProperty(attr.ScopeProperty);
                    if (scopeInfo == null)
                        throw new Exception("Invalid ScopeProperty");
                    object scope = scopeInfo.GetValue(this, null);
                    ICriterion criterion1 = Expression.Eq(attr.ScopeProperty, scope);
                    criterions = new ICriterion[] {criterion, criterion1};
                }
                T[] SimilarSlugs = MyActiveRecord<T>.FindAll(criterions);

                List<string> similarStrings = new List<string>();
                foreach (T similarSlug in SimilarSlugs)
                {
                    similarStrings.Add((string) slugInfo.GetValue(similarSlug, null));
                }

                string slug = GetUniqueSlug(attr, proposal, similarStrings);
                slugInfo.SetValue(this, slug, null);
            }    
        }

        /// <summary>
        /// Gets a unique value for the slug by adding an incemented number at the end.
        /// Override in classes to get a different behavior
        /// </summary>
        /// <param name="attr"></param>
        /// <param name="proposal"></param>
        /// <param name="similarStrings"></param>
        /// <returns></returns>
        protected static string GetUniqueSlug(SluggableAttribute attr, string proposal, List<string> similarStrings)
        {
            int i = 1;
            string slug = proposal;
            while (similarStrings.Contains(slug))
            {
                slug = (proposal.EndsWith(attr.Separator)) ? proposal + i.ToString() : proposal + attr.Separator + i.ToString();
                i++;
            }
            return slug;
        }

        /// <summary>
        /// Replace all foreign characters and
        /// replace all characters not in a to z, 0 to 9 by a separator and
        /// change to lower case
        /// </summary>
        /// <param name="s"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        protected static string Slugify(string s, string separator)
        {
            string result = Romanize(s.Normalize());
            result = Regex.Replace(result, @"[^a-zA-Z0-9]+", separator, RegexOptions.Compiled).ToLower();
            if (result.Length > 1 && result.EndsWith(separator))
                result = result.Substring(0, result.Length - 1);
            if (result.Length > 1 && result.StartsWith(separator))
                result = result.Substring(1);
            return result;
        }

        /// <summary>
        /// Replace all foreign characters by a combination of
        /// letter from a-z, A-Z, 0-9
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        protected static string Romanize(string s)
        {
            if (charMap.Count == 0)
                FillCharMap();
            string result = string.Empty;
            for (int i = 0; i < s.Length; i++)
            {
                if (charMap.ContainsKey(s[i]))
                    result += charMap[s[i]];
                else
                    result += s[i];
            }
            return result;
        }

        private static void FillCharMap()
        {
            charMap.Add('\x1f26', "i");
            charMap.Add('\x1f27', "i");
            charMap.Add('\x1fd8', "I");
            charMap.Add('\x1fd9', "I");
            charMap.Add('\x1fda', "I");
            charMap.Add('\x1fdb', "I");
            charMap.Add('\x1fdd', "");
            charMap.Add('\x1fde', "");
            charMap.Add('\x1fdf', "");
            charMap.Add('\x1fd0', "i");
            charMap.Add('\x1fd1', "i");
            charMap.Add('\x1fd2', "i");
            charMap.Add('\x1fd3', "i");
            charMap.Add('\x044b', "Y");
            charMap.Add('\x044c', "b");
            charMap.Add('\x1fd6', "i");
            charMap.Add('\x1fd7', "i");
            charMap.Add('\x1fc8', "E");
            charMap.Add('\x1fc9', "E");
            charMap.Add('\x1fca', "I");
            charMap.Add('\x1fcb', "I");
            charMap.Add('\x1fcc', "I");
            charMap.Add('\x1fcd', "");
            charMap.Add('\x1fce', "");
            charMap.Add('\x1fcf', "");
            charMap.Add('\x1fc0', "");
            charMap.Add('\x1fc1', "");
            charMap.Add('\x1fc2', "i");
            charMap.Add('\x1fc3', "i");
            charMap.Add('\x1fc4', "i");
            charMap.Add('\x1fc6', "i");
            charMap.Add('\x1fc7', "i");
            charMap.Add('\x1ff8', "O");
            charMap.Add('\x1ff9', "O");
            charMap.Add('\x1ffa', "O");
            charMap.Add('\x1ffb', "O");
            charMap.Add('\x1ffc', "O");
            charMap.Add('\x1ffe', "");
            charMap.Add('\x1ff2', "o");
            charMap.Add('\x1ff3', "o");
            charMap.Add('\x1ff4', "o");
            charMap.Add('\x1ff6', "o");
            charMap.Add('\x1ff7', "o");
            charMap.Add('\x1fe8', "Y");
            charMap.Add('\x1fe9', "Y");
            charMap.Add('\x1fea', "Y");
            charMap.Add('\x1feb', "Y");
            charMap.Add('\x1fec', "R");
            charMap.Add('\x1fed', "");
            charMap.Add('\x1fee', "");
            charMap.Add('\x1fef', "");
            charMap.Add('\x1fe0', "y");
            charMap.Add('\x1fe1', "y");
            charMap.Add('\x1fe2', "y");
            charMap.Add('\x1fe3', "y");
            charMap.Add('\x1fe4', "r");
            charMap.Add('\x1fe5', "r");
            charMap.Add('\x1fe6', "y");
            charMap.Add('\x1fe7', "y");
            charMap.Add('\x1f98', "I");
            charMap.Add('\x1f99', "I");
            charMap.Add('\x1f9a', "I");
            charMap.Add('\x1f9b', "I");
            charMap.Add('\x1f9c', "I");
            charMap.Add('\x1f9d', "I");
            charMap.Add('\x1f9e', "I");
            charMap.Add('\x1f9f', "I");
            charMap.Add('\x1f90', "i");
            charMap.Add('\x1f91', "i");
            charMap.Add('\x1f92', "i");
            charMap.Add('\x1f93', "i");
            charMap.Add('\x1f94', "i");
            charMap.Add('\x1f95', "i");
            charMap.Add('\x1f96', "i");
            charMap.Add('\x1f97', "i");
            charMap.Add('\x1f88', "A");
            charMap.Add('\x1f89', "A");
            charMap.Add('\x1f8a', "A");
            charMap.Add('\x1f8b', "A");
            charMap.Add('\x1f8c', "A");
            charMap.Add('\x1f8d', "A");
            charMap.Add('\x1f8e', "A");
            charMap.Add('\x1f8f', "A");
            charMap.Add('\x1f80', "a");
            charMap.Add('\x1f81', "a");
            charMap.Add('\x1f82', "a");
            charMap.Add('\x1f83', "a");
            charMap.Add('\x1f84', "a");
            charMap.Add('\x1f85', "a");
            charMap.Add('\x1f86', "a");
            charMap.Add('\x1f87', "a");
            charMap.Add('\x1fb8', "A");
            charMap.Add('\x1fb9', "A");
            charMap.Add('\x1fba', "A");
            charMap.Add('\x1fbb', "A");
            charMap.Add('\x1fbc', "A");
            charMap.Add('\x1fbd', "");
            charMap.Add('\x1fbf', "");
            charMap.Add('\x1fb0', "a");
            charMap.Add('\x1fb1', "a");
            charMap.Add('\x1fb2', "a");
            charMap.Add('\x1fb3', "a");
            charMap.Add('\x1fb4', "a");
            charMap.Add('\x1fb6', "a");
            charMap.Add('\x1fb7', "a");
            charMap.Add('\x1fa8', "O");
            charMap.Add('\x1fa9', "O");
            charMap.Add('\x1faa', "O");
            charMap.Add('\x1fab', "O");
            charMap.Add('\x1fac', "O");
            charMap.Add('\x1fad', "O");
            charMap.Add('\x1fae', "O");
            charMap.Add('\x1faf', "O");
            charMap.Add('\x1fa0', "o");
            charMap.Add('\x1fa1', "o");
            charMap.Add('\x1fa2', "o");
            charMap.Add('\x1fa3', "o");
            charMap.Add('\x1fa4', "o");
            charMap.Add('\x1fa5', "o");
            charMap.Add('\x1fa6', "o");
            charMap.Add('\x1fa7', "o");
            charMap.Add('\x037a', "");
            charMap.Add('\x10d8', "i");
            charMap.Add('\x10d9', "k");
            charMap.Add('\x10da', "l");
            charMap.Add('\x10db', "m");
            charMap.Add('\x10dc', "n");
            charMap.Add('\x10dd', "o");
            charMap.Add('\x10de', "p");
            charMap.Add('\x10df', "zh");
            charMap.Add('\x10d0', "a");
            charMap.Add('\x10d1', "b");
            charMap.Add('\x10d2', "g");
            charMap.Add('\x10d3', "d");
            charMap.Add('\x10d4', "e");
            charMap.Add('\x10d5', "v");
            charMap.Add('\x10d6', "z");
            charMap.Add('\x10d7', "t");
            charMap.Add('\x10f0', "h");
            charMap.Add('\x10e8', "sh");
            charMap.Add('\x10e9', "ch");
            charMap.Add('\x10ea', "ts");
            charMap.Add('\x10eb', "dz");
            charMap.Add('\x10ec', "ts");
            charMap.Add('\x10ed', "ch");
            charMap.Add('\x10ee', "kh");
            charMap.Add('\x10ef', "j");
            charMap.Add('\x10e0', "r");
            charMap.Add('\x10e1', "s");
            charMap.Add('\x10e2', "t");
            charMap.Add('\x10e3', "");
            charMap.Add('\x10e4', "p");
            charMap.Add('\x10e5', "k");
            charMap.Add('\x10e6', "gh");
            charMap.Add('\x10e7', "q");
            charMap.Add('\x03c8', "ps");
            charMap.Add('\x03c9', "o");
            charMap.Add('\x03ca', "i");
            charMap.Add('\x03cb', "y");
            charMap.Add('\x03cc', "o");
            charMap.Add('\x03cd', "y");
            charMap.Add('\x03ce', "o");
            charMap.Add('\x03c0', "p");
            charMap.Add('\x03c1', "r");
            charMap.Add('\x03c2', "s");
            charMap.Add('\x03c3', "s");
            charMap.Add('\x03c4', "t");
            charMap.Add('\x03c5', "y");
            charMap.Add('\x03c6', "f");
            charMap.Add('\x03c7', "x");
            charMap.Add('\x0451', "E");
            charMap.Add('\x0398', "TH");
            charMap.Add('\x0399', "I");
            charMap.Add('\x039a', "K");
            charMap.Add('\x039b', "L");
            charMap.Add('\x039c', "M");
            charMap.Add('\x039d', "N");
            charMap.Add('\x039e', "KS");
            charMap.Add('\x039f', "O");
            charMap.Add('\x0390', "i");
            charMap.Add('\x0391', "A");
            charMap.Add('\x0392', "B");
            charMap.Add('\x0393', "G");
            charMap.Add('\x0394', "D");
            charMap.Add('\x0395', "E");
            charMap.Add('\x0396', "Z");
            charMap.Add('\x0397', "I");
            charMap.Add('\x0388', "E");
            charMap.Add('\x0389', "I");
            charMap.Add('\x038a', "I");
            charMap.Add('\x038c', "O");
            charMap.Add('\x038e', "Y");
            charMap.Add('\x038f', "O");
            charMap.Add('\x0384', "");
            charMap.Add('\x0385', "");
            charMap.Add('\x0386', "A");
            charMap.Add('\x03b8', "th");
            charMap.Add('\x03b9', "i");
            charMap.Add('\x03ba', "k");
            charMap.Add('\x03bb', "l");
            charMap.Add('\x03bc', "m");
            charMap.Add('\x03bd', "n");
            charMap.Add('\x03be', "ks");
            charMap.Add('\x03bf', "o");
            charMap.Add('\x03b0', "y");
            charMap.Add('\x03b1', "a");
            charMap.Add('\x03b2', "b");
            charMap.Add('\x03b3', "g");
            charMap.Add('\x03b4', "d");
            charMap.Add('\x03b5', "e");
            charMap.Add('\x03b6', "z");
            charMap.Add('\x03b7', "i");
            charMap.Add('\x03a8', "PS");
            charMap.Add('\x03a9', "O");
            charMap.Add('\x03aa', "I");
            charMap.Add('\x03ab', "Y");
            charMap.Add('\x03ac', "a");
            charMap.Add('\x03ad', "e");
            charMap.Add('\x03ae', "i");
            charMap.Add('\x03af', "i");
            charMap.Add('\x03a0', "P");
            charMap.Add('\x03a1', "R");
            charMap.Add('\x0411', "B");
            charMap.Add('\x03a3', "S");
            charMap.Add('\x03a4', "T");
            charMap.Add('\x03a5', "Y");
            charMap.Add('\x03a6', "F");
            charMap.Add('\x03a7', "X");
            charMap.Add('\x0417', "Z");
            charMap.Add('\x0401', "E");
            charMap.Add('\x0438', "I");
            charMap.Add('\x0439', "I");
            charMap.Add('\x043a', "K");
            charMap.Add('\x043b', "L");
            charMap.Add('\x043c', "M");
            charMap.Add('\x043d', "N");
            charMap.Add('\x043e', "O");
            charMap.Add('\x043f', "P");
            charMap.Add('\x0430', "A");
            charMap.Add('\x0431', "B");
            charMap.Add('\x0432', "V");
            charMap.Add('\x0433', "G");
            charMap.Add('\x0434', "D");
            charMap.Add('\x0435', "E");
            charMap.Add('\x0436', "ZH");
            charMap.Add('\x0437', "Z");
            charMap.Add('\x0428', "SH");
            charMap.Add('\x0429', "SHCH");
            charMap.Add('\x042a', "b");
            charMap.Add('\x042b', "Y");
            charMap.Add('\x042c', "b");
            charMap.Add('\x042d', "E");
            charMap.Add('\x042e', "Y");
            charMap.Add('\x042f', "YA");
            charMap.Add('\x0420', "R");
            charMap.Add('\x0421', "S");
            charMap.Add('\x0422', "T");
            charMap.Add('\x0423', "");
            charMap.Add('\x0424', "F");
            charMap.Add('\x0425', "KH");
            charMap.Add('\x0426', "TS");
            charMap.Add('\x0427', "CH");
            charMap.Add('\x00d8', "O");
            charMap.Add('\x00d9', "U");
            charMap.Add('\x00da', "U");
            charMap.Add('\x00db', "U");
            charMap.Add('\x00dc', "Ue");
            charMap.Add('\x00dd', "Y");
            charMap.Add('\x00de', "TH");
            charMap.Add('\x00df', "ss");
            charMap.Add('\x00d0', "D");
            charMap.Add('\x00d1', "N");
            charMap.Add('\x00d2', "O");
            charMap.Add('\x00d3', "O");
            charMap.Add('\x00d4', "O");
            charMap.Add('\x00d5', "O");
            charMap.Add('\x00d6', "Oe");
            charMap.Add('\x00c8', "E");
            charMap.Add('\x00c9', "E");
            charMap.Add('\x00ca', "E");
            charMap.Add('\x00cb', "E");
            charMap.Add('\x00cc', "I");
            charMap.Add('\x00cd', "I");
            charMap.Add('\x00ce', "I");
            charMap.Add('\x00cf', "I");
            charMap.Add('\x00c0', "A");
            charMap.Add('\x00c1', "A");
            charMap.Add('\x00c2', "A");
            charMap.Add('\x00c3', "A");
            charMap.Add('\x00c4', "Ae");
            charMap.Add('\x00c5', "A");
            charMap.Add('\x00c6', "A");
            charMap.Add('\x00c7', "C");
            charMap.Add('\x00f8', "o");
            charMap.Add('\x00f9', "u");
            charMap.Add('\x00fa', "u");
            charMap.Add('\x00fb', "u");
            charMap.Add('\x00fc', "ue");
            charMap.Add('\x00fd', "y");
            charMap.Add('\x00fe', "th");
            charMap.Add('\x00ff', "y");
            charMap.Add('\x00f0', "d");
            charMap.Add('\x00f1', "n");
            charMap.Add('\x00f2', "o");
            charMap.Add('\x00f3', "o");
            charMap.Add('\x00f4', "o");
            charMap.Add('\x00f5', "o");
            charMap.Add('\x00f6', "oe");
            charMap.Add('\x00e8', "e");
            charMap.Add('\x00e9', "e");
            charMap.Add('\x00ea', "e");
            charMap.Add('\x00eb', "e");
            charMap.Add('\x00ec', "i");
            charMap.Add('\x00ed', "i");
            charMap.Add('\x00ee', "i");
            charMap.Add('\x00ef', "i");
            charMap.Add('\x00e0', "a");
            charMap.Add('\x00e1', "a");
            charMap.Add('\x00e2', "a");
            charMap.Add('\x00e3', "a");
            charMap.Add('\x00e4', "ae");
            charMap.Add('\x00e5', "a");
            charMap.Add('\x00e6', "ae");
            charMap.Add('\x00e7', "c");
            charMap.Add('\x00a8', "");
            charMap.Add('\x0158', "R");
            charMap.Add('\x0159', "r");
            charMap.Add('\x015a', "S");
            charMap.Add('\x015b', "s");
            charMap.Add('\x015c', "S");
            charMap.Add('\x015e', "S");
            charMap.Add('\x0150', "O");
            charMap.Add('\x0151', "o");
            charMap.Add('\x0152', "OE");
            charMap.Add('\x0153', "oe");
            charMap.Add('\x0154', "R");
            charMap.Add('\x0155', "r");
            charMap.Add('\x0156', "R");
            charMap.Add('\x0157', "r");
            charMap.Add('\x0148', "n");
            charMap.Add('\x0149', "n");
            charMap.Add('\x014a', "N");
            charMap.Add('\x014b', "n");
            charMap.Add('\x014c', "O");
            charMap.Add('\x014d', "o");
            charMap.Add('\x014e', "O");
            charMap.Add('\x014f', "o");
            charMap.Add('\x0140', "l");
            charMap.Add('\x0141', "L");
            charMap.Add('\x0142', "l");
            charMap.Add('\x0143', "N");
            charMap.Add('\x0144', "n");
            charMap.Add('\x0145', "N");
            charMap.Add('\x0146', "n");
            charMap.Add('\x0147', "N");
            charMap.Add('\x0178', "Y");
            charMap.Add('\x0179', "Z");
            charMap.Add('\x017a', "z");
            charMap.Add('\x017b', "Z");
            charMap.Add('\x017c', "z");
            charMap.Add('\x017d', "Z");
            charMap.Add('\x017e', "z");
            charMap.Add('\x017f', "ss");
            charMap.Add('\x0170', "U");
            charMap.Add('\x0171', "u");
            charMap.Add('\x0172', "U");
            charMap.Add('\x0173', "y");
            charMap.Add('\x0174', "W");
            charMap.Add('\x0175', "w");
            charMap.Add('\x0176', "Y");
            charMap.Add('\x0177', "y");
            charMap.Add('\x0168', "U");
            charMap.Add('\x0169', "u");
            charMap.Add('\x016a', "U");
            charMap.Add('\x016b', "u");
            charMap.Add('\x016c', "U");
            charMap.Add('\x016d', "u");
            charMap.Add('\x016e', "U");
            charMap.Add('\x016f', "u");
            charMap.Add('\x0160', "S");
            charMap.Add('\x0161', "s");
            charMap.Add('\x0162', "T");
            charMap.Add('\x0164', "T");
            charMap.Add('\x0165', "t");
            charMap.Add('\x0166', "T");
            charMap.Add('\x0118', "E");
            charMap.Add('\x0119', "e");
            charMap.Add('\x011a', "E");
            charMap.Add('\x011b', "e");
            charMap.Add('\x011c', "G");
            charMap.Add('\x011d', "g");
            charMap.Add('\x011e', "G");
            charMap.Add('\x011f', "g");
            charMap.Add('\x0110', "D");
            charMap.Add('\x0111', "d");
            charMap.Add('\x0112', "E");
            charMap.Add('\x0113', "e");
            charMap.Add('\x0114', "E");
            charMap.Add('\x0115', "e");
            charMap.Add('\x0116', "E");
            charMap.Add('\x0117', "e");
            charMap.Add('\x0108', "C");
            charMap.Add('\x0109', "c");
            charMap.Add('\x010a', "C");
            charMap.Add('\x010b', "c");
            charMap.Add('\x010c', "C");
            charMap.Add('\x010d', "c");
            charMap.Add('\x010e', "D");
            charMap.Add('\x010f', "d");
            charMap.Add('\x0100', "A");
            charMap.Add('\x0101', "a");
            charMap.Add('\x0102', "A");
            charMap.Add('\x0103', "a");
            charMap.Add('\x0104', "A");
            charMap.Add('\x0105', "a");
            charMap.Add('\x0106', "C");
            charMap.Add('\x0107', "c");
            charMap.Add('\x0138', "k");
            charMap.Add('\x0139', "K");
            charMap.Add('\x013a', "l");
            charMap.Add('\x013b', "K");
            charMap.Add('\x013c', "l");
            charMap.Add('\x013d', "K");
            charMap.Add('\x013e', "l");
            charMap.Add('\x013f', "K");
            charMap.Add('\x0130', "I");
            charMap.Add('\x0131', "i");
            charMap.Add('\x0132', "IJ");
            charMap.Add('\x0133', "ij");
            charMap.Add('\x0134', "J");
            charMap.Add('\x0135', "j");
            charMap.Add('\x0136', "K");
            charMap.Add('\x0137', "k");
            charMap.Add('\x0128', "I");
            charMap.Add('\x0129', "i");
            charMap.Add('\x012a', "I");
            charMap.Add('\x012b', "i");
            charMap.Add('\x012c', "I");
            charMap.Add('\x012d', "i");
            charMap.Add('\x012e', "I");
            charMap.Add('\x012f', "i");
            charMap.Add('\x0120', "G");
            charMap.Add('\x0121', "g");
            charMap.Add('\x0122', "G");
            charMap.Add('\x0123', "g");
            charMap.Add('\x0124', "H");
            charMap.Add('\x0125', "h");
            charMap.Add('\x0126', "H");
            charMap.Add('\x0127', "h");
            charMap.Add('\x1f75', "i");
            charMap.Add('\x1f77', "i");
            charMap.Add('\x0448', "SH");
            charMap.Add('\x0449', "SHCH");
            charMap.Add('\x044a', "");
            charMap.Add('\x0192', "f");
            charMap.Add('\x044d', "E");
            charMap.Add('\x044e', "Y");
            charMap.Add('\x044f', "YA");
            charMap.Add('\x0440', "R");
            charMap.Add('\x0441', "S");
            charMap.Add('\x0442', "T");
            charMap.Add('\x0443', "y");
            charMap.Add('\x0444', "F");
            charMap.Add('\x0445', "KH");
            charMap.Add('\x0446', "TS");
            charMap.Add('\x0447', "CH");
            charMap.Add('\x0418', "I");
            charMap.Add('\x0419', "I");
            charMap.Add('\x041a', "K");
            charMap.Add('\x041b', "L");
            charMap.Add('\x041c', "M");
            charMap.Add('\x041d', "N");
            charMap.Add('\x041e', "O");
            charMap.Add('\x041f', "P");
            charMap.Add('\x0410', "A");
            charMap.Add('\x0412', "V");
            charMap.Add('\x0413', "G");
            charMap.Add('\x0414', "D");
            charMap.Add('\x0415', "E");
            charMap.Add('\x0416', "ZH");
            charMap.Add('\x1f59', "Y");
            charMap.Add('\x1f5b', "Y");
            charMap.Add('\x1f5d', "Y");
            charMap.Add('\x1f5f', "Y");
            charMap.Add('\x1f50', "y");
            charMap.Add('\x1f51', "y");
            charMap.Add('\x1f52', "y");
            charMap.Add('\x1f53', "y");
            charMap.Add('\x1f54', "y");
            charMap.Add('\x1f55', "y");
            charMap.Add('\x1f56', "y");
            charMap.Add('\x1f57', "y");
            charMap.Add('\x1f48', "O");
            charMap.Add('\x1f49', "O");
            charMap.Add('\x1f4a', "O");
            charMap.Add('\x1f4b', "O");
            charMap.Add('\x1f4c', "O");
            charMap.Add('\x1f4d', "O");
            charMap.Add('\x1f40', "o");
            charMap.Add('\x1f41', "o");
            charMap.Add('\x1f42', "o");
            charMap.Add('\x1f43', "o");
            charMap.Add('\x1f44', "o");
            charMap.Add('\x1f45', "o");
            charMap.Add('\x1f78', "o");
            charMap.Add('\x1f79', "o");
            charMap.Add('\x1f7a', "y");
            charMap.Add('\x1f7b', "y");
            charMap.Add('\x1f7c', "o");
            charMap.Add('\x1f7d', "o");
            charMap.Add('\x1f70', "a");
            charMap.Add('\x1f71', "a");
            charMap.Add('\x1f72', "e");
            charMap.Add('\x1f73', "e");
            charMap.Add('\x1f74', "i");
            charMap.Add('\x0218', "S");
            charMap.Add('\x1f76', "i");
            charMap.Add('\x021a', "T");
            charMap.Add('\x1f68', "O");
            charMap.Add('\x1f69', "O");
            charMap.Add('\x1f6a', "O");
            charMap.Add('\x1f6b', "O");
            charMap.Add('\x1f6c', "O");
            charMap.Add('\x1f6d', "O");
            charMap.Add('\x1f6e', "O");
            charMap.Add('\x1f6f', "O");
            charMap.Add('\x1f60', "o");
            charMap.Add('\x1f61', "o");
            charMap.Add('\x1f62', "o");
            charMap.Add('\x1f63', "o");
            charMap.Add('\x1f64', "o");
            charMap.Add('\x1f65', "o");
            charMap.Add('\x1f66', "o");
            charMap.Add('\x1f67', "o");
            charMap.Add('\x1f18', "E");
            charMap.Add('\x1f19', "E");
            charMap.Add('\x1f1a', "E");
            charMap.Add('\x1f1b', "E");
            charMap.Add('\x1f1c', "E");
            charMap.Add('\x1f1d', "E");
            charMap.Add('\x1f10', "e");
            charMap.Add('\x1f11', "e");
            charMap.Add('\x1f12', "e");
            charMap.Add('\x1f13', "e");
            charMap.Add('\x1f14', "e");
            charMap.Add('\x1f15', "e");
            charMap.Add('\x1f08', "A");
            charMap.Add('\x1f09', "A");
            charMap.Add('\x1f0a', "A");
            charMap.Add('\x1f0b', "A");
            charMap.Add('\x1f0c', "A");
            charMap.Add('\x1f0d', "A");
            charMap.Add('\x1f0e', "A");
            charMap.Add('\x1f0f', "A");
            charMap.Add('\x1f00', "a");
            charMap.Add('\x1f01', "a");
            charMap.Add('\x1f02', "a");
            charMap.Add('\x1f03', "a");
            charMap.Add('\x1f04', "a");
            charMap.Add('\x1f05', "a");
            charMap.Add('\x1f06', "a");
            charMap.Add('\x1f07', "a");
            charMap.Add('\x1f38', "I");
            charMap.Add('\x1f39', "I");
            charMap.Add('\x1f3a', "I");
            charMap.Add('\x1f3b', "I");
            charMap.Add('\x1f3c', "I");
            charMap.Add('\x1f3d', "I");
            charMap.Add('\x1f3e', "I");
            charMap.Add('\x1f3f', "I");
            charMap.Add('\x1f30', "i");
            charMap.Add('\x1f31', "i");
            charMap.Add('\x1f32', "i");
            charMap.Add('\x1f33', "i");
            charMap.Add('\x1f34', "i");
            charMap.Add('\x1f35', "i");
            charMap.Add('\x1f36', "i");
            charMap.Add('\x1f37', "i");
            charMap.Add('\x1f28', "I");
            charMap.Add('\x1f29', "I");
            charMap.Add('\x1f2a', "I");
            charMap.Add('\x1f2b', "I");
            charMap.Add('\x1f2c', "I");
            charMap.Add('\x1f2d', "I");
            charMap.Add('\x1f2e', "I");
            charMap.Add('\x1f2f', "I");
            charMap.Add('\x1f20', "i");
            charMap.Add('\x1f21', "i");
            charMap.Add('\x1f22', "i");
            charMap.Add('\x1f23', "i");
            charMap.Add('\x1f24', "i");
            charMap.Add('\x1f25', "i");
        }
    }
    
    /// <summary>
    /// Attribute to set the sluggable properties
    /// </summary>
    [Serializable, AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
    public class SluggableAttribute : Attribute
    {
        // Fields
        private readonly string proposalProperty;
        private readonly string slugProperty;
        private readonly string scopeProperty;
        private readonly string separator;

        // Methods

        public SluggableAttribute(string proposalProperty, string slugProperty)
            :
            this(proposalProperty, slugProperty, null, "-")
        {

        }

        public SluggableAttribute(string proposalProperty, string slugProperty, string scopeProperty):
            this(proposalProperty, slugProperty, scopeProperty, "-")
        {
            
        }

        public SluggableAttribute(string proposalProperty, string slugProperty, string scopeProperty, string separator)
        {
            if (String.IsNullOrEmpty(proposalProperty))
            {
                throw new ArgumentNullException("proposalProperty", "Invalid proposalProperty");
            }
            if (String.IsNullOrEmpty(slugProperty))
            {
                throw new ArgumentNullException("slugProperty", "Invalid slugProperty");
            }
            if (String.IsNullOrEmpty(separator))
            {
                throw new ArgumentNullException("separator", "separator");
            }
            this.proposalProperty = proposalProperty;
            this.slugProperty = slugProperty;
            this.scopeProperty = scopeProperty;
            this.separator = separator;
        }

        // Properties
        public string ProposalProperty
        {
            get
            {
                return this.proposalProperty;
            }
        }
        public string SlugProperty
        {
            get
            {
                return this.slugProperty;
            }
        }
        public string ScopeProperty
        {
            get
            {
                return this.scopeProperty;
            }
        }
        public string Separator
        {
            get
            {
                return this.separator;
            }
        }

       }
}
